import Axios, { type InternalAxiosRequestConfig } from "axios";
import { stringify, parse } from "qs";
import type {
  AxiosInstance,
  AxiosRequestConfig,
  CustomParamsSerializer,
  ParamEncoder,
} from "axios";
import type { RequestMethods, HttpRequestConfig, HttpResponse } from "./type";
import type HttpTy from "./type";
import { cloneDeep, isFunction } from "lodash-unified";
import { deepMerge } from "@/utils/index";
import { axiosHooks } from "./axiosHooks";
import { AxiosCanceler } from "./axiosCancel";

// 相关配置请参考：www.axios-js.com/zh-cn/docs/#axios-request-config-1
const defaultConfig: AxiosRequestConfig = {
  // 请求超时时间
  timeout: 1000 * 10,
  headers: {
    Accept: "application/json, text/plain, */*",
    "Content-Type": "application/json",
    "X-Requested-With": "XMLHttpRequest",
  },
  // 数组格式参数序列化（https://github.com/axios/axios/issues/5142）
  paramsSerializer: {
    encode: parse as ParamEncoder,
    serialize: stringify as unknown as CustomParamsSerializer,
  },
};

class Http {
  /** 当前Axios实例对象 */
  private axiosInstance: AxiosInstance;

  /** 配置对象 */
  private readonly options: HttpRequestConfig;

  constructor(opt: HttpRequestConfig) {
    this.options = opt;
    this.axiosInstance = Axios.create(deepMerge(defaultConfig, opt || {}));
    this.setupInterceptors();
  }

  /**
   * @description:  创建 axios 实例
   */
  private createAxios(config: HttpRequestConfig): void {
    this.axiosInstance = Axios.create(config);
  }

  /**
   * 获取 axios 实例
   */
  public getAxios(): AxiosInstance {
    return this.axiosInstance;
  }

  /**
   * @description: 重新配置 axios 实例
   */
  configAxios(config: HttpRequestConfig) {
    if (!this.axiosInstance) {
      return;
    }
    this.createAxios(config);
  }

  /**
   * @description: 设置 axios 实例 header
   */
  setHeader(headers: any): void {
    if (!this.axiosInstance) {
      return;
    }
    Object.assign(this.axiosInstance.defaults.headers, headers);
  }

  /**
   * @description 初始化 axios 拦截器
   */
  private setupInterceptors() {
    const interceptors = this.options.interceptors;
    if (!interceptors) {
      return;
    }
    const {
      requestInterceptors,
      requestInterceptorsCatch,
      responseInterceptors,
      responseInterceptorsCatch,
    } = interceptors;

    const axiosCanceler = new AxiosCanceler();

    /** 请求拦截器 */
    requestInterceptors &&
      isFunction(requestInterceptors) &&
      this.axiosInstance.interceptors.request.use(
        (config: InternalAxiosRequestConfig<any>) => {
          axiosCanceler.addPending(config);
          return requestInterceptors(config, this.options);
        }
      );

    /** 请求拦截错误处理 */
    requestInterceptorsCatch &&
      isFunction(requestInterceptorsCatch) &&
      this.axiosInstance.interceptors.request.use(
        undefined,
        requestInterceptorsCatch
      );

    /** 响应拦截器 */
    responseInterceptors &&
      isFunction(responseInterceptors) &&
      this.axiosInstance.interceptors.response.use((res: HttpResponse) => {
        res && axiosCanceler.removePending(res.config);
        return responseInterceptors(res);
      }, undefined);

    /** 响应拦截错误处理 */
    responseInterceptorsCatch &&
      isFunction(responseInterceptorsCatch) &&
      this.axiosInstance.interceptors.response.use(undefined, (error) => {
        return responseInterceptorsCatch(this.axiosInstance, error);
      });
  }

  /** 通用请求工具函数 */
  public request<T>(
    methodOrConfig: HttpRequestConfig | RequestMethods,
    url: string,
    // axios 原生参数
    param?: AxiosRequestConfig,
    // 部分自定义参数
    axiosConfig?: HttpRequestConfig
  ): Promise<T> {
    let config: HttpRequestConfig = {};
    if (arguments.length === 1) {
      config = methodOrConfig as HttpRequestConfig;
    } else {
      config = {
        method: methodOrConfig as RequestMethods,
        url,
        ...param,
        ...axiosConfig,
      };
    }
    let conf = cloneDeep(config);
    const { beforeRequestHook, beforeResponseHook } = axiosHooks || {};

    // 请求前数据预处理
    if (beforeRequestHook && isFunction(beforeRequestHook)) {
      conf = beforeRequestHook(conf, config);
    }
    // 单独处理自定义请求/响应回掉
    return new Promise((resolve, reject) => {
      this.axiosInstance
        .request(conf)
        .then((response: any) => {
          if (beforeResponseHook && isFunction(beforeResponseHook)) {
            try {
              const re = beforeResponseHook(response, conf);
              resolve(re);
            } catch (error) {
              reject(error || new Error("request error!"));
            }
          }
          resolve(response as unknown as Promise<T>);
        })
        .catch((error) => {
          reject(error);
        });
    });
  }

  /** 单独抽离的post工具函数 */
  public post<T, P>(
    url: string,
    params?: AxiosRequestConfig<T>,
    config?: HttpRequestConfig
  ): Promise<P> {
    return this.request<P>("post", url, params, config);
  }

  /** 单独抽离的get工具函数 */
  public get<T, P>(
    url: string,
    params?: AxiosRequestConfig<T>,
    config?: HttpRequestConfig
  ): Promise<P> {
    return this.request<P>("get", url, params, config);
  }
}

const createAxiosOpt = () => {
  const {
    requestInterceptors,
    responseInterceptors,
    responseInterceptorsCatch,
  } = axiosHooks;
  return {
    interceptors: {
      requestInterceptors,
      responseInterceptors,
      responseInterceptorsCatch,
    },
  };
};
export const http: HttpTy = new Http(createAxiosOpt());
